package accesoRepositorio;

import java.util.LinkedList;
import java.util.List;
import modelo.gestionCarta.Producto;
import modelo.gestionPedidos.Consumicion;
import modelo.gestionPedidos.Pedido;
import modelo.gestionReserva.Mesa;
import interfacesRemotas.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.SQLException;
//import com.mysql.jdbc.ResultSet;



/**
 * Clase que implementa el acceso a los datos contenidos en las tablas
 * relacionadas con la información de los pedidos.
 * 
 * @author Carmen Pastor Morales
 * @version 1.0
 * @created 3-may-2007
 *
 */
public class AccesoPedido extends UnicastRemoteObject implements InterfazPedido 
	
	{
	
	private static final long serialVersionUID = 1L;


	/**
	 * Consutructor de la clase
	 */
	public AccesoPedido() throws RemoteException
		{
		super();
		}
	
	/**
	 * Devuelve el pedido correspondiente al identificador pasado como parametro
	 * @param idPedido
	 * @throws RemoteException
	 * @return Pedido
	 */
	public Pedido getPedido( int idPedido ) throws RemoteException
	{
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs1;
		int numComensales;
		List<Mesa> listaMesas = new LinkedList<Mesa>();
		List<Consumicion> listaConsumiciones = new LinkedList<Consumicion>();
		Pedido pedido;
		
		if(!bd.abrir())
			return null;
			
		rs1=bd.select("SELECT num_comensales FROM Pedido WHERE id_pedido="+idPedido+";");
		if(rs1==null)
			{
			bd.cerrar();
			return null;
			}
		
		try {
			//OJO! Nos situamos en la primera fila del ResultSet
			rs1.first();
			numComensales=rs1.getInt("num_comensales");
		} catch (SQLException e) {
			System.err.println("Error al recuperar un pedido: "+e.getMessage());
			bd.cerrar();
			return null;
			}
		
		//Buscamos las consumiciones correspondientes al pedido para rellenar la lista de cosumiciones
		listaConsumiciones=this.getConsumiciones(idPedido);
		//Recuperamos las mesas correspondientes al pedido
		listaMesas=this.getMesas(idPedido);
		
		// Ya hemos recuperado todos los datos necesarios para crear el obj Pedido
		pedido=new Pedido(idPedido, numComensales, listaMesas, listaConsumiciones);
		bd.cerrar();
		return pedido;
	}
	
	/**
	 * Este metodo devuelve todos pedidos activos en el sistema
	 * del Restaurante
	 * @param idRestaurante Restaurante al que pertenece el pedido
	 * @throws RemoteException
	 * @return Lista de pedidos
	 */
	public List<Pedido> getPedidos(int idRestaurante) throws RemoteException
	{
		ConexionBD bd=new ConexionBD();
		int idPedido;
		Pedido pedido;
		java.sql.ResultSet rs;
		List<Pedido> listaPedidos = new LinkedList<Pedido>();
		if(!bd.abrir())
			return null;
		rs=bd.select("SELECT id_pedido FROM Pedido WHERE id_restaurante="+idRestaurante+";");
		if(rs==null)
			{
			bd.cerrar();
			return null;
			}
		try {
			//OJO! El ResultSet empieza apuntando ANTES de la primera fila
			while(rs.next())
				{
				idPedido=rs.getInt("id_pedido");
				pedido=this.getPedido(idPedido);
				if(pedido!=null)
					listaPedidos.add(pedido);
				}
			} catch (SQLException e) {
				System.err.println("Error al recuperar los datos de una lista de pedidos: "+e.getMessage());
				bd.cerrar();
				return null;
			}
		bd.cerrar();
		return listaPedidos;
	}

	/**
	 * Este método devuelve todas las mesas asociadas a un pedido
	 * @param idPedido
	 * @throws RemoteException
	 * @return Lista de mesas
	 */
	public List<Mesa> getMesas(int idPedido) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs=null;
		int idMesa=0;
		Mesa mesa;
		List<Mesa> listaMesas = new LinkedList<Mesa>();
		AccesoMesa am=new AccesoMesa();
		
		if(!bd.abrir())
			return null;
		rs=bd.select("SELECT num_mesa FROM esta_asociada WHERE id_pedido="+idPedido+";");
		if(rs==null)
			{
			bd.cerrar();
			return null;
			}
		try {
				
			while(rs.next())
				{
				//Recuperamos los datos de una mesa
				idMesa=rs.getInt("num_mesa");
				mesa=am.getMesa(idMesa);
				if(mesa!=null)
					listaMesas.add(mesa);	
				}
		} catch (SQLException e) {
			System.err.println("Error en AccesoPedido recuperando una lista de mesas para un pedido: "+e.getMessage());
			bd.cerrar();
			return null;
		}
		bd.cerrar();
		return listaMesas;
	}

	/*public int getNumComensales(int idPedido)
		{
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs=null;
		int numCOmensales=-1;
		
		if(!bd.abrir())
			return -1;
		bd.select("SELECT num_comensales FROM Pedido WHERE id_pedido="+idPedido+";");
		}*/
	
	/**
	 * Recupera la consumicion correspondiente al id de consumicion que 
	 * se le pasa como parametro 
	 * @param idConsu
	 * @return Consumicion
	 * @throws RemoteException
	 */
	public Consumicion getConsumicion(int idConsu) throws RemoteException
	{
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs;
		int idProd, estadoConsu, numComensal;
		String obs;
		AccesoCarta ac=new AccesoCarta();
		Consumicion consu;
		
		if(!bd.abrir())
			return null;
		
		rs=bd.select("SELECT id_producto, estado, num_comensal," +
				"observaciones FROM Consumicion WHERE id_consumicion="+idConsu+";");
		if(rs==null)
			{
			bd.cerrar();
			return null;
			}
		try {
			rs.first();
			//idConsu=rs.getInt("id_consumicion");
			idProd = rs.getInt("id_producto");
			estadoConsu = rs.getInt("estado");
			numComensal = rs.getInt("num_comensal");
			obs = rs.getString("observaciones");
			} catch (SQLException e) {
				System.err.println("Error en AccesoPedido al recuperar una Cosumicion: "+e.getMessage());
				bd.cerrar();
				return null;
		}
		Producto producto = ac.getProducto(idProd);
		if (producto==null)
			{
			bd.cerrar();
			return null;
			}
		consu=new Consumicion(idConsu, estadoConsu,numComensal, obs,producto);
		bd.cerrar();
		return consu;
	}
	
	
	
	/**
	 * Se recuperan las consumiciones correspondientes a un  pedido
	 * @param idPedido
	 * @return Lista  consumiciones
	 * @throws RemoteException
	 */
	public List<Consumicion> getConsumiciones(int idPedido) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs=null;
		int idConsu=0;
		Consumicion consu;
		List<Consumicion> listaConsumiciones = new LinkedList<Consumicion>();
		
		if(!bd.abrir())
			return null;
		rs=bd.select("SELECT id_consumicion FROM  Consumicion WHERE id_pedido="+idPedido+";");
		if(rs==null)
			{
			bd.cerrar();
			return null;
			}
		
		try {
					
			while(rs.next())
				{
				//Recuperamos los datos de una consumicion
				idConsu=rs.getInt("id_consumicion");
				consu=this.getConsumicion(idConsu);
				if(consu!=null)
					listaConsumiciones.add(consu);	
				}
		} catch (SQLException e) {
			System.err.println("Error en AccesoPedido al recuperar la lista de consumiciones de un Pedido: "+e.getMessage());
			bd.cerrar();
			return null;
		}
		bd.cerrar();
		return listaConsumiciones;
	}
	
	/**
	 * Se recuperan todas las consumiciones listas del pedido
	 * @param idPedido
	 * @return Lista  consumiciones listas
	 * @throws RemoteException
	 */
	public List<Consumicion> getConsumicionesListas(int idPedido) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		java.sql.ResultSet rs=null;
		int idConsu=0;
		Consumicion consu;
		List<Consumicion> listaConsumicionesListas = new LinkedList<Consumicion>();
		
		if(!bd.abrir())
			return null;
		rs=bd.select("SELECT id_consumicion FROM  Consumicion WHERE id_pedido="+idPedido+" AND estado="+3+";");
		if(rs==null)
			{
			bd.cerrar();
			return null;
			}
		try {	
			while(rs.next())
				{
				//Recuperamos los datos de una consumicion
				idConsu=rs.getInt("id_consumicion");
				consu=this.getConsumicion(idConsu);
				if(consu!=null)
					listaConsumicionesListas.add(consu);	
				}
		} catch (SQLException e) {
			System.err.println("Error en AccesoPedido al recuperar la lista de consumiciones listas de un Pedido: "+e.getMessage());
			bd.cerrar();
			return null;
		}
		bd.cerrar();
		return listaConsumicionesListas;
	}
	/**
	 * Borra una consumicion de la base de datos
	 * @param codConsu
	 * @return boolean Cierto si se ha borrado correctamente
	 */
	public boolean deleteConsumicion(int codConsu) throws RemoteException
		{
		ConexionBD bd=new ConexionBD();
		boolean borrada=false;
		if(!bd.abrir())
			return false;
		borrada=bd.update("DELETE FROM Consumicion WHERE id_consumicion="+codConsu+";");
		bd.cerrar();
		return borrada;
		}
	/**
	 * Borra un pedido de la base de datos. Se debe dejar libres las mesas
	 * que tenia asociadas
	 * @param idPedido Identificador del pedido a borrar
	 * @throws RemoteException
	 * @return boolean Cierto su el pedido se ha borrado correctamente
	 * 
	 */
	public boolean deletePedido(int idPedido) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		boolean borrado=false;
		AccesoMesa am=new AccesoMesa();
		boolean cambiadoEstado=false,error=false;
		
		Mesa mesa;
		Pedido pedido;
		if(!bd.abrir())
			return false;
		
		pedido=this.getPedido(idPedido);
		if(pedido==null)
			{
			bd.cerrar();
			return false;
			}
		
		//Recorro la lista de mesas para ponerlas a 'ocupado' y para incluir en 
		//la tabla esta_asociada cada mesa con el pedido
		Object[] arrayMesas=this.getMesas(idPedido).toArray();
		int indice=0;
		for(int i=0; i<arrayMesas.length;i++)
			{	
			mesa=(Mesa)arrayMesas[i];
			cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"libre");
			if(!cambiadoEstado)
				{//Si no puedo cambiar el estado de todas las mesas las mesas borro las que ya he cambiado
				System.out.println("Error en AccesoPedido al liberar las mesas antes de borrar un pedido.");
				indice=i;
				error=true;
				//Nos salimos del bucle
				i=arrayMesas.length;
				}
			}
		//Si se da un error tengo que cambiar el estado de las mesas que 
		//ya habia dejado libres de nuevo a 'ocupado'
		if(error)
			{
			for(int i=0; i<indice;i++)
				{
				mesa=(Mesa)arrayMesas[i];
				cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"ocupado");
				}
			bd.cerrar();
			return false;
			}
		
		//Una vez liberadas las mesas ya podemos borrar el pedido
		//a su vez se borrarán todas las consumiciones asociadas
		//al pedido
		borrado=bd.update("DELETE FROM Pedido WHERE id_pedido="+idPedido+";");
		bd.cerrar();
		return borrado;
	}

	/**
	 * Inserta un pedido nuevo en la base de datos. Para insertar un pedido en la base de 
	 * datos debemos crear tambien la lista de mesas que tendrá asociado y la lista 
	 * de consumiciones tambien si las hubiera
	 * @param idRestaurante
	 * @param numComensales
	 * @param listaMesas
	 * @param listaConsumiciones
	 * @throws RemoteException
	 * @return boolean Cierto si se ha insertado correctamente el pedido en la base 
	 * de datos
	 */
	public boolean insertPedido(int idRestaurante,int numComensales, List<Mesa> listaMesas) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		boolean insertado=false,cambiadoEstado=false;
		boolean asociadaMesa=false,error=false;
		java.sql.ResultSet rs;
		int idPedido;
		Mesa mesa;
		AccesoMesa am=new AccesoMesa();
		
		if(!bd.abrir())
			return false;
		
		//Inserto un nuevo pedido en la base de datos
		insertado=bd.update("INSERT INTO Pedido (num_comensales, id_restaurante)" +
				" VALUES ("+numComensales+","+idRestaurante+");");
		if(!insertado)
			{
			bd.cerrar();
			return false;
			}
		//Debo recuperar el identificador que se le ha asignado al Pedido
		//que será el id mayor
		rs=bd.select("Select MAX(id_pedido) FROM Pedido;");
		try {
			rs.first();
			idPedido=rs.getInt(1);
		} catch (SQLException e) {
			System.err.println("Error en AccesoPedido al insertar un nuevo pedido: "+e.getMessage());
			bd.cerrar();
			return false;
		}
		
		//Recorro la lista de mesas para ponerlas a 'ocupado' y para incluir en 
		//la tabla esta_asociada cada mesa con el pedido
		Object[] arrayMesas=listaMesas.toArray();
		int indice=0;
		for(int i=0; i<arrayMesas.length;i++)
			{	
			mesa=(Mesa)arrayMesas[i];
			cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"ocupado");
			asociadaMesa=this.asociarMesa(idPedido,mesa.getNumMesa());
			if(!cambiadoEstado || !asociadaMesa)
				{//Si no puedo cambiar el estado de las mesas borro las que ya he cambiado
				System.out.println("Error en AccesoPedido al cambiar estado de las mesas o asociarlas a un nuevo pedido.");
				indice=i;
				error=true;
				//Nos salimos del bucle para tratar el error
				i=arrayMesas.length;
				}
			}
		//Si se da un error tengo que cambiar el estado de las mesas que 
		//ya habia ocupado a libre y borrar el pedido (cuando se borre el pedido
		//se borrarán automáticamente las entradas correspondientes en la tabla esta_asociada)
		if(error)
			{
			for(int i=0; i<indice;i++)
				{
				mesa=(Mesa)arrayMesas[i];
				cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"libre");
				}
			this.deletePedido(idPedido);
			bd.cerrar();
			return false;
			}
		bd.cerrar();
		return true;
	}

	/**
	 * Se almacena una nueva consumicion el la base de datos
	 * @param idProd
	 * @param estado
	 * @param estado
	 * @param numComensal
	 * @param observaciones
	 * @throws RemoteException
	 * @return boolean Cierto si se ha insertado correctamente
	 */
	public boolean insertConsumicion(int idProd,int idPedido,  int estado, int numComensal, String observaciones) throws RemoteException {
		ConexionBD bd=new ConexionBD();
		boolean insertada=false;
		
		if(!bd.abrir())
			return false;
		insertada=bd.update("INSERT INTO Consumicion (id_producto, id_pedido, estado, num_comensal, observaciones) " +
				" VALUES ("+idProd+","+idPedido+","+estado+","+numComensal+",'"+observaciones+"');");
		bd.cerrar();
		return insertada;
	}
	/**
	 * Este método agrega una entrada a la tabla esta_asociada para asociar
	 * una mesa a un pedido 
	 * @param idPedido
	 * @param mesa
	 * @throws RemoteException
	 * @return boolean Cierto si se asocia la mesa al pedido
	 */
	private boolean asociarMesa(int idPedido, int numMesa) throws RemoteException
	{
		ConexionBD bd=new ConexionBD();
		boolean agregada=false;
		if(!bd.abrir())
			return false;
		agregada=bd.insert("INSERT INTO esta_asociada VALUES ("+idPedido+","+numMesa+");");
		bd.cerrar();
		return agregada;
	}
	
	/**
	 * Este método agrega borra una entrada de la tabla esta_asociada
	 * @param mesa
	 * @throws RemoteException
	 * @return boolean Cierto si se borra
	 */
	private boolean deleteAsociarMesa(int numMesa) throws RemoteException
	{
		ConexionBD bd=new ConexionBD();
		boolean borrada=false;
		if(!bd.abrir())
			return false;
		borrada=bd.insert("DELETE FROM INTO esta_asociada WHERE num_mesa="+numMesa+";");
		bd.cerrar();
		return borrada;
	}
	
	/**
	 * Actualiza un consumicion en la base de datos. Una consumicion nunca podrá
	 * cambiarse de pedido
	 * @param idConsu
	 * @param estado
	 * @param numComensal
	 * @param observaciones
	 * @return boolean Cierto si se ha actualizado la consumicion en la base de datos
	 */
	public boolean updateConsumicion(int idConsu, int estado, int numComensal, String observaciones, Producto p) throws RemoteException {
		
		ConexionBD bd=new ConexionBD();
		boolean actualizada=false;
		if(!bd.abrir())
			return false;
		actualizada=bd.update("UPDATE Consumicion SET id_producto="+p.consultarIdProducto()+", estado="+estado+", num_comensal="+numComensal+
				", observaciones='"+observaciones+"' WHERE id_consumicion="+idConsu+";");
		bd.cerrar();
		return actualizada;
	}

	/**
	 * Metodo que almacena los datos de un pedido que ha sido ampliado
	 * @param ListaMesas Nuevas mesas para asignar al pedido. Se deben ocupar las  mesas y
	 * crear las entradas en tabla esta_asociada para asociarlas a un pedido
	 * @param idPedido
	 * @param numComensales
	 * @param listaMesas Nuevas mesas para asignar a un pedido
	 * @param listaConsumiciones Nuevas consumiciones para incluir en un pedido
	 * @throws RemoteException
	 * @return Cierto si se ha ampliado correctamente el pedido
	 */
	public boolean ampliarPedido(int idPedido, int numComensales, List<Mesa> listaMesasTodas, List<Consumicion> listaConsumicionesTodas) throws RemoteException 
		{
		ConexionBD bd=new ConexionBD();
		boolean actualizado=false,cambiadoEstado=false;
		boolean error=false,asociadaMesa=false,creadaConsumicion=false;
		Mesa mesa;
		Consumicion consu;
		AccesoMesa am=new AccesoMesa();
		if(!bd.abrir())
			return false;
		
		//	Recupero las consumiciones que ya tenia el pedido asignadas
		List<Consumicion> listaConsumiciones=this.getConsumiciones(idPedido);	
		
		//Si se cumple este if significa que hay consumiciones nuevas en el pedido
		if( listaConsumicionesTodas.size()>listaConsumiciones.size())
			{
			//Obtengo una lista solo con las consumiciones nuevas
			List<Consumicion> listaConsumicionesNuevas = listaConsumicionesTodas.subList(listaConsumiciones.size(), listaConsumicionesTodas.size());
		
			//	Creamos las nuevas consumiciones
			Object[] arrayCosumiciones=listaConsumicionesNuevas.toArray();
			int indice=0;
			for(int i=0; i<arrayCosumiciones.length;i++)
				{
				consu=(Consumicion)arrayCosumiciones[i];
				creadaConsumicion=this.insertConsumicion(consu.getCodConsumicion(), idPedido, consu.getEstado(), consu.getNumComensal(), consu.getObservaciones());			
				if(!creadaConsumicion)
					{//Si no puedo crear la consumicion borro las que he creado antes
					System.out.println("Error en AccesoPedido al incluir las nuevas consumiciones en un pedido ampliado.");
					indice=i;
					error=true;
					//	Nos salimos del bucle
					i=arrayCosumiciones.length;
					}
				}
			//Si se da un error tengo que borrar las cosumiciones que habia crado
			if(error)
				{
				for(int i=0; i<indice;i++)
					{
					consu=(Consumicion)arrayCosumiciones[i];
					this.deleteConsumicion(consu.getCodConsumicion());
					}
				bd.cerrar();
				return false;
				}
			}
		
		//	Recupero las mesas que ya tenia el pedido asignadas
		List<Mesa> listaMesas=this.getMesas(idPedido);	
		
		//Si se cumple este if significa que hay mesas nuevas en el pedido
		if( listaMesasTodas.size()>listaMesas.size())
			{
			//Obtengo una lista solo con las mesas nuevas
			List<Mesa> listaMesasNuevas = listaMesasTodas.subList(listaMesas.size(), listaMesasTodas.size());
			
			//Recorro la lista de mesas para ponerlas a 'ocupado' y para incluir en 
			//la tabla esta_asociada cada mesa con el pedido
			Object[] arrayMesas=listaMesasNuevas.toArray();
			int indice=0;
			for(int i=0; i<arrayMesas.length;i++)
				{	
				mesa=(Mesa)arrayMesas[i];
				cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"ocupado");
				asociadaMesa=this.asociarMesa(idPedido,mesa.getNumMesa());
				if(!cambiadoEstado || !asociadaMesa)
					{//Si no puedo cambiar el estado de las mesas borro las que ya he cambiado
					System.out.println("Error en AccesoPedido al cambiar estado de las mesas o asociarlas a un pedido ampliado.");
					indice=i;
					error=true;
					//	Nos salimos del bucle
					i=arrayMesas.length;
					}
				}
			//Si se da un error tengo que cambiar el estado de las mesas que 
			//ya habia ocupado a libre y eliminar las correspondecias en la
			//tabla esta_asociada
			if(error)
				{
				for(int i=0; i<indice;i++)
					{
					mesa=(Mesa)arrayMesas[i];
					cambiadoEstado=am.updateEstadoMesa(mesa.getNumMesa(),"libre");
					this.deleteAsociarMesa(mesa.getNumMesa());
					}
				bd.cerrar();
				return false;
				}
			}
		
		actualizado=bd.update("UPDATE Pedido SET num_comensales="+numComensales+" WHERE id_pedido="+idPedido+";");
		bd.cerrar();
		return actualizado;	
			}
	
	}
	
